package io.hellsinger.vortex.ui.screen.storage.editor

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.View
import android.view.View.GONE
import android.view.View.OnApplyWindowInsetsListener
import android.view.View.VISIBLE
import android.view.WindowInsets
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams
import android.widget.FrameLayout.LayoutParams.MATCH_PARENT
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import io.hellsinger.navigation.screen.ScreenController
import io.hellsinger.viewmodel.BitViewModel
import io.hellsinger.viewmodel.BitViewModel.Companion.IDLE
import io.hellsinger.vortex.R
import io.hellsinger.vortex.bit.Bits.Companion.hasFlag
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.data.model.name
import io.hellsinger.vortex.di.createViewModel
import io.hellsinger.vortex.foundation.bundle.parcelable
import io.hellsinger.vortex.navigation.globalUiProvider
import io.hellsinger.vortex.ui.component.InfoView
import io.hellsinger.vortex.ui.component.LoadingView
import io.hellsinger.vortex.ui.component.StorageTextEditorView
import io.hellsinger.vortex.ui.component.navigation.NavigationBarView
import io.hellsinger.vortex.ui.component.observeInsets
import io.hellsinger.vortex.ui.dsl.frame
import io.hellsinger.vortex.ui.dsl.info
import io.hellsinger.vortex.ui.dsl.loading
import io.hellsinger.vortex.ui.icon.Icons
import io.hellsinger.vortex.ui.withCompatInsets

class StorageFileEditorScreenController(
    private val context: Context,
) : ScreenController<PathItem>(),
    BitViewModel.UiStateUpdateCollector<UiState>,
    OnApplyWindowInsetsListener {
    private var viewModel: StorageFileEditorViewModel? = null

    private var root: FrameLayout? = null

    private var bar: NavigationBarView? = globalUiProvider(context)?.bar

    private var editor: StorageTextEditorView? = null
    private var info: InfoView? = null
    private var loading: LoadingView? = null

    private var animator: ValueAnimator? = null

    override fun onBackActivatedImpl(): Boolean = false

    override fun onCreateViewImpl(context: Context): View {
        root =
            frame(context) {
                layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT)

                editor =
                    StorageTextEditorView(context).apply {
                        id = R.id.file_editor_field
                        layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT)
                        visibility = GONE
                        observeInsets(this@StorageFileEditorScreenController)
                    }
                addView(editor, 0)

                info =
                    info(index = 1) {
                        layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT)
                        visibility = GONE
                    }

                loading =
                    loading(index = 2) {
                        layoutParams =
                            LayoutParams(MATCH_PARENT, MATCH_PARENT).apply {
                                gravity = Gravity.CENTER
                            }
                        visibility = GONE
                    }
            }

        return root!!
    }

    override fun onApplyWindowInsets(
        view: View,
        insets: WindowInsets,
    ): WindowInsets =
        withCompatInsets(insets) {
            val navigationBar = getInsets(WindowInsetsCompat.Type.navigationBars())
            val statusBar = getInsets(WindowInsetsCompat.Type.statusBars())

            when (view.id) {
                R.id.file_editor_field ->
                    view.updatePadding(
                        top = statusBar.top,
                        bottom = navigationBar.bottom,
                    )
            }
            return insets
        }

    override fun onPrepareImpl() {
        if (viewModel == null) {
            viewModel = context.createViewModel()
        }

        viewModel?.collectUiState(this)

        args?.apply {
            bar?.title = name
//            navigator?.bar?.replaceItems(listOf(NavigationActions.More, NavigationActions.Search))
            viewModel?.load(this)
        }
    }

    override fun onFocusImpl() {
    }

    override fun onHideImpl() {
        viewModel?.cancelCollectUiState()
//        navigator?.bar?.subtitle = null
    }

    override fun onDestroyImpl() {
        viewModel?.onDestroy()
        viewModel = null
        root?.removeAllViews()
        editor?.destroy()
        root = null
        editor = null
    }

    override suspend fun emit(
        event: Int,
        state: UiState,
    ) {
        if (event == IDLE) return
        if (event hasFlag StorageFileEditorViewModel.UPDATE_EDITOR) {
            editor?.setLines(state.lines)
            navigateField()
        }
        if (event hasFlag StorageFileEditorViewModel.UPDATE_LOADING) navigateLoading()
        if (event hasFlag StorageFileEditorViewModel.UPDATE_MESSAGE) {
            if (event hasFlag StorageFileEditorViewModel.REASON_ITEM_TOO_LARGE) {
                info?.icon = Icons.Rounded.File
                info?.message = "${state.item?.path?.getNameAt()} is too long"
            } else if (event hasFlag StorageFileEditorViewModel.REASON_ITEM_DOES_NOT_EXIST) {
                info?.icon = Icons.Rounded.File
                info?.message = "${state.item?.path?.getNameAt()} does not exist"
            } else if (event hasFlag StorageFileEditorViewModel.REASON_ITEM_IS_NOT_READABLE) {
                info?.icon = Icons.Rounded.File
                info?.message = "${state.item?.path?.getNameAt()} is not readable"
            }
            navigateInfo()
        }
    }

    private fun createSwitchAnimator(duration: Long = 150L): ValueAnimator {
        if (animator != null) return animator!!

        val animator = ValueAnimator.ofFloat(0F, 1F)
        animator.duration = duration
        this.animator = animator
        return animator
    }

    private fun navigateField() {
        if ((editor?.visibility == VISIBLE)) return
        animator?.cancel()
        createSwitchAnimator()

        animator?.addUpdateListener { anim ->
            if (loading?.visibility == VISIBLE) loading?.alpha = 1F - anim.animatedFraction
            if (info?.visibility == VISIBLE) info?.alpha = 1F - anim.animatedFraction
            editor?.alpha = anim.animatedFraction
        }

        animator?.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    editor?.alpha = 1F
                    editor?.visibility = VISIBLE
                    loading?.visibility = GONE
                    info?.visibility = GONE
                    animator = null
                }

                override fun onAnimationStart(animation: Animator) {
                    editor?.visibility = GONE
                }

                override fun onAnimationEnd(animation: Animator) {
                    editor?.alpha = 1F
                    editor?.visibility = VISIBLE
                    loading?.visibility = GONE
                    info?.visibility = GONE
                    animator = null
                }
            },
        )

        animator?.start()
    }

    private fun navigateLoading() {
        if (loading?.visibility == VISIBLE) return
        animator?.cancel()
        createSwitchAnimator()

        animator?.addUpdateListener { anim ->
            if (editor?.visibility == VISIBLE) editor?.alpha = 1F - anim.animatedFraction
            if (info?.visibility == VISIBLE) info?.alpha = 1F - anim.animatedFraction
            loading?.alpha = anim.animatedFraction
        }

        animator?.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    loading?.alpha = 1F
                    loading?.visibility = VISIBLE
                    info?.visibility = GONE
                    editor?.visibility = GONE
                    animator = null
                }

                override fun onAnimationStart(animation: Animator) {
                    loading?.visibility = GONE
                }

                override fun onAnimationEnd(animation: Animator) {
                    loading?.alpha = 1F
                    loading?.visibility = VISIBLE
                    editor?.visibility = GONE
                    info?.visibility = GONE
                    animator = null
                }
            },
        )

        animator?.start()
    }

    private fun navigateInfo() {
        if (loading?.visibility == VISIBLE) return
        animator?.cancel()
        createSwitchAnimator()

        animator?.addUpdateListener { anim ->
            if (editor?.visibility == VISIBLE) editor?.alpha = 1F - anim.animatedFraction
            if (loading?.visibility == VISIBLE) info?.alpha = 1F - anim.animatedFraction
            info?.alpha = anim.animatedFraction
        }

        animator?.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    info?.alpha = 1F
                    info?.visibility = VISIBLE
                    loading?.visibility = GONE
                    editor?.visibility = GONE
                    animator = null
                }

                override fun onAnimationStart(animation: Animator) {
                    info?.visibility = GONE
                }

                override fun onAnimationEnd(animation: Animator) {
                    info?.alpha = 1F
                    info?.visibility = VISIBLE
                    editor?.visibility = GONE
                    loading?.visibility = GONE
                    animator = null
                }
            },
        )

        animator?.start()
    }

    private fun createPrefixBundle(prefix: String): String = prefix + "storage_file_editor_item"

    override fun onSaveImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean {
        val current = viewModel?.getUiState()?.item ?: return false

        buffer.putParcelable(createPrefixBundle(prefix), current)
        return true
    }

    override fun onRestoreImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean {
        val item = buffer.parcelable<PathItem>(createPrefixBundle(prefix)) ?: return false
        setArgs(item)
        return true
    }
}
